package com.xiangxiao.rpan.storage.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import com.xiangxiao.rpan.storage.constant.ResponseCode;
import com.xiangxiao.rpan.storage.constant.ResponseMessage;
import com.xiangxiao.rpan.storage.dto.*;
import com.xiangxiao.rpan.storage.enums.FileTypeEnum;
import com.xiangxiao.rpan.storage.enums.StorageTypeEnum;
import com.xiangxiao.rpan.storage.exception.StorageExcetion;
import com.xiangxiao.rpan.storage.factory.StorageSourceContext;
import com.xiangxiao.rpan.storage.service.AbstractBaseFileService;
import com.xiangxiao.rpan.storage.service.DownloadService;
import com.xiangxiao.rpan.storage.service.UploadService;
import com.xiangxiao.rpan.storage.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * @auther xiangxiao
 * @email 573768011@qq.com
 * @data 2023/9/6 14:26
 */
@Slf4j
@RestController
@RequestMapping("/api/file")
public class TransmissionFileController {
  private static final Logger logger = LoggerFactory.getLogger(TransmissionFileController.class);

  @Autowired
  private UploadService uploadService;
  @Autowired
  private DownloadService downloadService;
  @Resource
  private StorageSourceContext storageSourceContext;

  @PostMapping("/operator/upload/file")
  public JsonObject bigFileUploadMulti(@RequestPart("file") MultipartFile file,
                                       @RequestParam("fileMD5") String fileMD5, @RequestParam("fileSize") Long fileSize,
                                       @RequestParam("totalChunks") int totalChunks, @RequestParam("currChunks") int currChunks) {
    try {
      BigFileBean bigFile = uploadService.getParametersFromRequest(file, fileMD5, fileSize, totalChunks, currChunks);
      BigFileDto dto = uploadService.uploadBigFile(bigFile);
      if (StringUtils.isBlank(dto.getFileId()) || dto.getFileId() == null) {
        return new JsonObject(ResponseMessage.BIG_FILE_UPLOAD_FAIL, ResponseCode.FAIL);
      } else {
        return new JsonObject(ResponseMessage.BIG_FILE_UPLOAD_SUCCESS, ResponseCode.OK, dto);
      }
    } catch (Exception e) {
      e.printStackTrace();
      logger.error("系统错误，上传大文件异常", e);
      return new JsonObject(ResponseMessage.BIG_FILE_UPLOAD_FAIL, ResponseCode.FAIL);
    }
  }

  @GetMapping("/download")
  public ResponseEntity<JsonObject> downLoadFile(HttpServletRequest request, HttpServletResponse response) {
    String fileAbsolutePath = request.getParameter("path");
    File file = new File(fileAbsolutePath);
    String fileName = "";
    MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;

    HttpHeaders headers = new HttpHeaders();
    headers.setContentDispositionFormData("attachment", StringUtil.encodeAllIgnoreSlashes(fileName));
    if (StringUtils.isEmpty(file.getName())) {
      fileName = file.getName();
    };
    try {
      downloadService.downloadFile(request, response);
      return ResponseEntity
          .ok()
          .headers(headers)
          .contentLength(file.length())
          .contentType(mediaType)
          .body(null);
    } catch (Exception e) {
      logger.error(ResponseMessage.DOWNLOAD_FILE_FAILED, e);
      return ResponseEntity
          .ok()
          .body(new JsonObject(ResponseMessage.DOWNLOAD_FILE_FAILED, ResponseCode.DOWNLOAD_FILE_FAILED));
    }
  }

  @PostMapping("/operator/delete/batch")
  public JsonObject batchDelete(@Valid @RequestBody BatchDeleteDto batchDeleteDto) {
    String key = batchDeleteDto.getStorageKey().replace("_storage", "");
    AbstractBaseFileService<?> fileService = storageSourceContext.getInitStorageBeanByStorageId(Enum.valueOf(StorageTypeEnum.class, key.toUpperCase()));
    List<BatchDeleteDto.DeleteItem> deleteItems = batchDeleteDto.getDeleteItems();

    int deleteSuccessCount = 0, deleteFailCount = 0, totalCount = CollUtil.size(deleteItems);
    Map<String, Boolean> pathCheckCache = new HashMap<>();
    for (BatchDeleteDto.DeleteItem deleteItem : deleteItems) {
      // 检查权限
      Boolean pathCheckResult = pathCheckCache.get(deleteItem.getPath());
      // 缓存值为 false, 则即为失败, 直接跳过此删除的文件
      if (BooleanUtil.isFalse(pathCheckResult)) {
        deleteFailCount++;
        continue;
      }
      // 缓存没有, 则进行校验
      if (pathCheckResult == null) {
        // todo
        // 文件、文件夹的密码检验
        pathCheckCache.put(deleteItem.getPath(), true);
      }
      boolean flag = false;
      try {
        if (deleteItem.getType() == FileTypeEnum.FILE) {
          flag = fileService.deleteFile(deleteItem.getPath(), deleteItem.getName());
        } else if (deleteItem.getType() == FileTypeEnum.FOLDER) {
          if (Enum.valueOf(StorageTypeEnum.class, key.toUpperCase()).equals(StorageTypeEnum.FASTDFS)) {
            return new JsonObject(ResponseMessage.NOT_SUPPORT_FASTDFS_DELETE_FLODER, ResponseCode.NOT_SUPPORT_FASTDFS_DELETE_FLODER);
          };
          flag = fileService.deleteFolder(deleteItem.getPath(), deleteItem.getName());
        }

        if (flag) {
          deleteSuccessCount++;
        } else {
          deleteFailCount++;
        }
      } catch (Exception e) {
        logger.error("删除文件/文件夹失败, 文件路径: {}, 文件名称: {}", deleteItem.getPath(), deleteItem.getName(), e);
        deleteFailCount++;
      }
    }

    if (totalCount > 1) {
      return new JsonObject("批量删除 " + totalCount + " 个, 删除成功 " + deleteSuccessCount + " 个, 失败 " + deleteFailCount + " 个.", ResponseCode.OK);
    } else {
      return totalCount == deleteSuccessCount ? new JsonObject("删除成功", ResponseCode.OK) : new JsonObject("删除失败", ResponseCode.BATCH_DELETE_FILE_FAILED);
    }

  }

  @PostMapping("/operator/rename/folder")
  public JsonObject renameFolder(@Valid @RequestBody RenameDto renameDto) {
    String key = renameDto.getStorageKey().replace("_storage", "");
    AbstractBaseFileService<?> fileService = storageSourceContext.getInitStorageBeanByStorageId(Enum.valueOf(StorageTypeEnum.class, key.toUpperCase()));

    return fileService.renameFolder(renameDto.getPath(), renameDto.getName(), renameDto.getNewName()) ?
        new JsonObject(ResponseMessage.RENAME_SUCCESS, ResponseCode.OK)
        :
        new JsonObject(ResponseMessage.RENAME_FAILED, ResponseCode.RENAME_FAILED);
  }

  @PostMapping("/operator/rename/file")
  public JsonObject renameFile(@Valid @RequestBody RenameDto renameDto) {
    String key = renameDto.getStorageKey().replace("_storage", "");
    AbstractBaseFileService<?> fileService = storageSourceContext.getInitStorageBeanByStorageId(Enum.valueOf(StorageTypeEnum.class, key.toUpperCase()));

    return fileService.renameFile(renameDto.getPath(), renameDto.getName(), renameDto.getNewName()) ?
          new JsonObject(ResponseMessage.RENAME_SUCCESS, ResponseCode.OK)
          :
        new JsonObject(ResponseMessage.RENAME_FAILED, ResponseCode.RENAME_FAILED);
  }
}
